using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Xna.Framework;
using Xfinity.Entities;
using Xfinity.AI.States;
using Xfinity.Scripting;

namespace Xfinity.AI
{
    public class Agent : Character
    {
        public Agent()
            : this("", "", null)
        {
        }
        private object instance;
        public override void Initialize()
        {

            base.Initialize();
        }
        private Behaviour behaviour;

        public Behaviour Behaviour
        {
            [DebuggerStepThrough]
            get { return behaviour; }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="Agent"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="typeName">Name of the type.</param>
        /// <param name="game">The game.</param>
        public Agent(string name, string typeName, Game game)
            : base(name, game)
        {
            behaviour = new Behaviour();
            behaviour.SetCharacterDisposition(this, float.MaxValue);
            AIManager manager = null;
            if (game != null)
            {
                manager = game.Services.GetService(typeof(AIManager)) as AIManager;
            }
            if (manager != null)
            {
                manager.Agents.Add(this);
            }
            this.typeName = typeName;
            scriptType = ScriptManager.CompiledScripts.GetType(typeName, true, true);
            ConstructorInfo con = scriptType.GetConstructor(new Type[] { typeof(string) });

            instance = con.Invoke(new object[] { Name });
            MethodInfo method = scriptType.GetMethod("Update", new Type[] { typeof(GameTime) });

            updateMethod = (UpdateDelegate)UpdateDelegate.CreateDelegate(typeof(UpdateDelegate), instance, method);

            this.typeName = typeName;
        }
        private UpdateDelegate updateMethod;
        private delegate void UpdateDelegate(GameTime gameTime);
        private string typeName;

        /// <summary>
        /// 
        /// </summary>
        public string TypeName
        {
            get { return typeName; }
            set { typeName = value; }
        }
	
        [NonSerialized]
        private StateStackCollection states = new StateStackCollection();

        /// <summary>
        /// Gets the states.
        /// </summary>
        /// <value>The states.</value>
        public StateStackCollection States
        {
            [DebuggerStepThrough]
            get { return states; }
        }

        /// <summary>
        /// Gets or sets the state of the current.
        /// </summary>
        /// <value>The state of the current.</value>
        public State CurrentState
        {
            [DebuggerStepThrough]
            get
            {
                if (states.Count >= 1)
                    return states.Peek();
                else
                    return State.Idle;
            }
            [DebuggerStepThrough]
            set { states.Push(value); }
        }

        private Script script;

        /// <summary>
        /// Gets or sets the script.
        /// </summary>
        /// <value>The script.</value>
        public Script Script
        {
            [DebuggerStepThrough]
            get { return script; }
            [DebuggerStepThrough]
            set { script = value; }
        }

        private Type scriptType;

        /// <summary>
        /// Called when the GameComponent needs to be updated.  Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Microsoft.Xna.Framework.GameComponent.Update(Microsoft.Xna.Framework.GameTime)</param>
        public override void Update(GameTime gameTime)
        {
            this.updateMethod(gameTime);

            base.Update(gameTime);
        }
    }

    /// <summary>
    /// The storage container for agent states.
    /// </summary>
    public class StateStackCollection : Stack<State>
    {
        public State this[int i]
        {
            get
            {
                State state = State.Idle;
                List<State> states = new List<State>();

                if (Count >= 1)
                {
                    for (int x = 0; x < i; x++)
                    {
                        states.Add(Pop());
                    }
                    state = Peek();
                    for (int x = 0; x < i; x++)
                    {
                        Push(states[i - x]);
                    }
                }
                return state;
            }
        }

        #region ICollection<State> Members

        public void Add(State item)
        {
            Push(item);
        }

        #endregion
    }
}